package zcatt.examples.jface.snippets;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ISynchronizable;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.reconciler.DirtyRegion;
import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
import org.eclipse.jface.text.reconciler.MonoReconciler;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationModel;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationAccessExtension;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.IAnnotationPresentation;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.VerticalRuler;
import org.eclipse.jface.text.source.inlined.AbstractInlinedAnnotation;
import org.eclipse.jface.text.source.inlined.Positions;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class Snippet013VerticalRuler {

	public static void main(String[] args) {
		Display display = new Display();
		Shell shell = new Shell(display);
		shell.setLayout(new GridLayout(2, true));
		shell.setText("Snippet013VerticalRuler");
		shell.setSize(800, 400);
		
		Composite comp1 = new Composite(shell, SWT.BORDER);
		comp1.setLayoutData(new GridData(GridData.FILL_BOTH));
		comp1.setLayout(new FillLayout());
		Composite comp2 = new Composite(shell, SWT.BORDER); 
		comp2.setLayoutData(new GridData(GridData.FILL_BOTH));
		comp2.setLayout(new FillLayout());

		
		case1(comp1);
		case2(comp2);
		
		shell.open();
		while(!shell.isDisposed())
		{
			if(!display.readAndDispatch())
				display.sleep();
		}
		shell.dispose();
		
	}
	
	static final int VerticalRulerWidth = 30;
	static SourceViewer sourceViewer1;
	static SourceViewer sourceViewer2;
	
	public static void case1(Composite parent)
	{
		VerticalRuler verticalRuler = createVerticalRuler1();
		sourceViewer1 = new SourceViewer(parent, verticalRuler, SWT.BORDER);
		sourceViewer1.setDocument(new Document(
				"line1, \n"
				+"line2, hello, the world\n"
				+"line3, good morning.\n"
				+"line4, hi!\n"
				+"line5, \n"
				+"line6, verticalRuler1\n"
				+"line7, snippet013\n"
				+"line8, example.\n"
				+"line9, \n"
				+"line10, eclipse\n"
				+"line11, sourceViewer\n"
				), new AnnotationModel());
		
		//connect sourceViewer with reconciler
		MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() {

			//document changed, so update
			@Override
			public void setDocument(IDocument document) {	
				System.err.println("setDocument"+document.get());
				Map<Annotation, Position> annPosMap = getAnnotationPositionMap1(sourceViewer1);
				updateAnnotations1(sourceViewer1, annPosMap);
			}

			
			@Override	//不会被执行到.
			public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
				// skip. 
				System.err.println("reconcile");
			}

			//dirty occurs, so update
			@Override
			public void reconcile(IRegion partition) {
				//System.err.println("partition="+partition.getOffset());
				Map<Annotation, Position> annPosMap = getAnnotationPositionMap1(sourceViewer1);
				updateAnnotations1(sourceViewer1, annPosMap);
			}
			
		}, false);		//false导致调用第二个reconcile()
		
		reconciler.setDelay(1);
		reconciler.install(sourceViewer1);		
	}
	
	static VerticalRuler createVerticalRuler1()
	{
		VerticalRuler res = new VerticalRuler(VerticalRulerWidth);
		return res;		
	}
	
	//用anns更新sourceViewer的annModel
	public static void updateAnnotations1(SourceViewer viewer, Map<? extends Annotation, ? extends Position> anns)
	{
		Iterator<? extends Entry<? extends Annotation, ? extends Position>> iter= anns.entrySet().iterator();
		while (iter.hasNext()) {
			Map.Entry<? extends Annotation, ? extends Position> mapEntry= iter.next();
			Annotation annotation= mapEntry.getKey();
			Position position= mapEntry.getValue();
			EvenLineAnnotation1 ann = (EvenLineAnnotation1)annotation;
			System.out.print(""+ann.line+", ");
		}
		System.out.println();
		System.out.println("size = "+anns.size());
		
		//此即AnnotationModel
		IAnnotationModel annModel = viewer.getAnnotationModel();
		IAnnotationModelExtension annModelExt = (IAnnotationModelExtension) annModel;
		

//		List<Annotation> annToRemove= new ArrayList<Annotation>();
//		
//		Iterator<Annotation> itr = annModel.getAnnotationIterator();
//		while(itr.hasNext())
//		{			
//			annToRemove.add(itr.next());			
//		}
//
//		Annotation[] array = annToRemove.toArray(new Annotation[annToRemove.size()]);
//		
//		for(Annotation a : array)
//		{
//			EvenLineAnnotation el = (EvenLineAnnotation) a;
//			System.out.println("to remove "+el.line);
//		}

		synchronized (getLockObject(annModel)) 
		{		
			//此处仅示意,更好的实现应当是仅删除不用和改变的anns, 再添加变更的和新增的 
			try {
				annModelExt.removeAllAnnotations();
				annModelExt.replaceAnnotations(null,  anns);				
//				annModelExt.replaceAnnotations(array,  anns);				
			}
			catch(ClassCastException e)
			{
				e.printStackTrace();
			}			
		}		
		
//		itr = annModel.getAnnotationIterator();
//		while(itr.hasNext())
//		{
//			EvenLineAnnotation ann = (EvenLineAnnotation)itr.next();
//			System.out.println("annInModel line="+ ann.line);
//		}
	}

	static Object getLockObject(IAnnotationModel annotationModel) {
		if (annotationModel instanceof ISynchronizable) {
			Object lock= ((ISynchronizable) annotationModel).getLockObject();
			if (lock != null)
				return lock;
		}
		return annotationModel;
	}
	
	//创建偶数行指示的annotations
	public static Map<Annotation, Position> getAnnotationPositionMap1(SourceViewer viewer)
	{
		Map<Annotation, Position> map = new HashMap<Annotation,Position>();
		
		IDocument doc = viewer.getDocument();
		int lineCount = doc.getNumberOfLines();
		
		for(int i = 0; i< lineCount; i+=2)
		{
			try {
				//若文档是以'\n结尾, 且取的是最后的空行, 则下面的pos会是无效pos, 
				//用于annModelExt.replaceAnnotations()会因BadLocationException而被干扰.
				if(i == lineCount -1 && doc.getLineLength(i) ==0)
					break;

				int offset= doc.getLineOffset(i);
				Position pos = new Position(offset, 1);
				System.out.println("line="+i+", offset="+pos.offset);
				EvenLineAnnotation1 ann = new EvenLineAnnotation1(i);
				map.put(ann, pos);
			}
			catch(BadLocationException e)
			{
//				e.printStackTrace();
			}
		}
		
		System.out.println("anns count="+map.size());
		return map;
	}
	
	//IAnnotationPresentation用于想verticalRuler表明this annotation使用自己paint()绘制.
	public static class EvenLineAnnotation1 extends Annotation implements IAnnotationPresentation
	{
		public int line;
		public EvenLineAnnotation1(int line)
		{
			super("EvenLineAnnotation1Type", false, String.valueOf(line));
			this.line = line;
		}
		
		@Override
		public int getLayer() {
			// TODO Auto-generated method stub
			return 0;
		}
		
		@Override
		public void paint(GC gc, Canvas canvas, Rectangle bounds) {
//			System.out.print("paint "+line+", ");
			gc.setBackground(new Color(255,255, 0));
			gc.fillRectangle(bounds);
			gc.drawText(String.valueOf(line), bounds.x, bounds.y);
		}
	}
	
	//以下是case2, 不同之处在于使用IAnnotationAccessExtension, 通过reflection调用annotation.paint()
	
	public static void case2(Composite parent)
	{
		VerticalRuler verticalRuler = createVerticalRuler2();
		
		sourceViewer2 = new SourceViewer(parent, verticalRuler, SWT.BORDER);
		sourceViewer2.setDocument(new Document(
				"line1, \n"
				+"line2, hello, the world\n"
				+"line3, good morning.\n"
				+"line4, hi!\n"
				+"line5, \n"
				+"line6, verticalRuler1\n"
				+"line7, snippet013\n"
				+"line8, example.\n"
				+"line9, \n"
				+"line10, eclipse\n"
				+"line11, sourceViewer\n"
				), new AnnotationModel());
		
		//connect sourceViewer with reconciler
		MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() {

			//document changed, so update
			@Override
			public void setDocument(IDocument document) {	
				System.err.println("setDocument"+document.get());
				Map<Annotation, Position> annPosMap = getAnnotationPositionMap2(sourceViewer2);
				updateAnnotations2(sourceViewer2, annPosMap);
			}

			
			@Override	//不会被执行到.
			public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
				// skip. 
				System.err.println("reconcile");
			}

			//dirty occurs, so update
			@Override
			public void reconcile(IRegion partition) {
				//System.err.println("partition="+partition.getOffset());
				Map<Annotation, Position> annPosMap = getAnnotationPositionMap2(sourceViewer2);
				updateAnnotations2(sourceViewer2, annPosMap);
			}
			
		}, false);		//false导致调用第二个reconcile()
		
		reconciler.setDelay(1);
		reconciler.install(sourceViewer2);		
	}
	
	static VerticalRuler createVerticalRuler2()
	{
		//MyAnnotationAccess实现了IAnnotationAccessExtension, 故会使用其paint()绘制annotation
		VerticalRuler res = new VerticalRuler(VerticalRulerWidth, new MyAnnotationAccess2());
		return res;		
	}
	
	//更新sourceViewer的annModel
	public static void updateAnnotations2(SourceViewer viewer, Map<? extends Annotation, ? extends Position> anns)
	{
		Iterator<? extends Entry<? extends Annotation, ? extends Position>> iter= anns.entrySet().iterator();
		while (iter.hasNext()) {
			Map.Entry<? extends Annotation, ? extends Position> mapEntry= iter.next();
			Annotation annotation= mapEntry.getKey();
			Position position= mapEntry.getValue();
			EvenLineAnnotation2 ann = (EvenLineAnnotation2)annotation;
			System.out.print(""+ann.line+", ");
		}
		System.out.println();
		System.out.println("size = "+anns.size());
		
		//此即AnnotationModel
		IAnnotationModel annModel = viewer.getAnnotationModel();
		IAnnotationModelExtension annModelExt = (IAnnotationModelExtension) annModel;
		

//		List<Annotation> annToRemove= new ArrayList<Annotation>();
//		
//		Iterator<Annotation> itr = annModel.getAnnotationIterator();
//		while(itr.hasNext())
//		{			
//			annToRemove.add(itr.next());			
//		}
//
//		Annotation[] array = annToRemove.toArray(new Annotation[annToRemove.size()]);
//		
//		for(Annotation a : array)
//		{
//			EvenLineAnnotation el = (EvenLineAnnotation) a;
//			System.out.println("to remove "+el.line);
//		}

		synchronized (getLockObject(annModel)) 
		{		
			//此处仅示意,更好的实现应当是仅删除改变的anns, 再添加变更的和新增的 
			try {
				annModelExt.removeAllAnnotations();
				annModelExt.replaceAnnotations(null,  anns);				
//				annModelExt.replaceAnnotations(array,  anns);				
			}
			catch(ClassCastException e)
			{
				e.printStackTrace();
			}			
		}		
		
//		itr = annModel.getAnnotationIterator();
//		while(itr.hasNext())
//		{
//			EvenLineAnnotation ann = (EvenLineAnnotation)itr.next();
//			System.out.println("annInModel line="+ ann.line);
//		}
	}
	
	//创建偶数行指示的annotations
	public static Map<Annotation, Position> getAnnotationPositionMap2(SourceViewer viewer)
	{
		Map<Annotation, Position> map = new HashMap<Annotation,Position>();
		
		IDocument doc = viewer.getDocument();
		int lineCount = doc.getNumberOfLines();
		
		for(int i = 0; i< lineCount; i+=2)
		{
			try {
				//若文档是以'\n结尾, 且取的是最后的空行, 则下面的pos会是无效pos, 
				//用于annModelExt.replaceAnnotations()会因BadLocationException而被干扰.
				if(i == lineCount -1 && doc.getLineLength(i) ==0)
					break;

				int offset= doc.getLineOffset(i);
				Position pos = new Position(offset, 1);
				System.out.println("line="+i+", offset="+pos.offset);
				EvenLineAnnotation2 ann = new EvenLineAnnotation2(i);
				map.put(ann, pos);
			}
			catch(BadLocationException e)
			{
//				e.printStackTrace();
			}
		}
		
		System.out.println("anns count="+map.size());
		return map;
	}
	
	public static class MyAnnotationAccess2 implements IAnnotationAccess, IAnnotationAccessExtension
	{

		@Override
		public String getTypeLabel(Annotation annotation) {
			return "MyAnnotationAccess2";
		}

		@Override
		public int getLayer(Annotation annotation) {

			//优先使用IAnnotationPresentation.getLayer()
			if (annotation instanceof IAnnotationPresentation) {
				IAnnotationPresentation presentation= (IAnnotationPresentation) annotation;
				
				return presentation.getLayer();
			}
			
			//reflection
			try {
				Method method= annotation.getClass().getMethod("getLayer"); 
				Integer result= (Integer) method.invoke(annotation);
				
				return result.intValue();
			} catch (SecurityException x) {
			} catch (IllegalArgumentException x) {
			} catch (NoSuchMethodException x) {
			} catch (IllegalAccessException x) {
			} catch (InvocationTargetException x) {
			}

			return IAnnotationAccessExtension.DEFAULT_LAYER;			
		}

		@Override
		public void paint(Annotation annotation, GC gc, Canvas canvas, Rectangle bounds) {
			//优先使用IAnnotationPresentation.getLayer()
			if (annotation instanceof IAnnotationPresentation) {
				IAnnotationPresentation presentation= (IAnnotationPresentation) annotation;
				presentation.paint(gc, canvas, bounds);		//zf, 转到annotationPresentation.paint()
				return;
			}

			//使用reflection, 直接调用annotation obj的paint(GC, Canvas, Rectangle)
			try {
				Method method= annotation.getClass().getMethod("paint", GC.class, Canvas.class, Rectangle.class);
				method.invoke(annotation, gc, canvas, bounds);			
			} catch (SecurityException x) {
			} catch (IllegalArgumentException x) {
			} catch (NoSuchMethodException x) {
			} catch (IllegalAccessException x) {
			} catch (InvocationTargetException x) {
			}
			
		}

		@Override
		public boolean isPaintable(Annotation annotation) {
			if (annotation instanceof IAnnotationPresentation)
				return true;

			boolean res = false;
			try {
				@SuppressWarnings("unused")
				Method method= annotation.getClass().getMethod("paint", GC.class, Canvas.class, Rectangle.class);
				res = true;
			} catch (SecurityException x) {
			} catch (IllegalArgumentException x) {
			} catch (NoSuchMethodException x) {
			}
			
			return res;
		}

		@Override
		public boolean isSubtype(Object annotationType, Object potentialSupertype) {
			return false;
		}

		@Override
		public Object[] getSupertypes(Object annotationType) {
			return null;
		}

		@Override
		public Object getType(Annotation annotation) {
			return annotation.getType();
		}

		@Override
		public boolean isMultiLine(Annotation annotation) {
			return true;
		}

		@Override
		public boolean isTemporary(Annotation annotation) {
			return !annotation.isPersistent();
		}
		
	}

	//与EvenLineAnnotation1不同的在于没有实现IAnnotationPresentation
	//但提供了paint() method, 供MyAnnotationAccess2.paint()调用
	public static class EvenLineAnnotation2 extends Annotation //implements IAnnotationPresentation
	{
		public int line;
		public EvenLineAnnotation2(int line)
		{
			super("EvenLineAnnotation2Type", false, String.valueOf(line));
			this.line = line;
		}
		
		public int getLayer() {
			return 0;
		}
		
		public void paint(GC gc, Canvas canvas, Rectangle bounds) {
//			System.out.print("paint "+line+", ");
			gc.setBackground(new Color(255,255, 0));
			gc.fillRectangle(bounds);
			gc.drawText(String.valueOf(line), bounds.x, bounds.y);
		}
	}
	
}
